"""
This file is part of GSBEdit. For copyright and licensing information, please
see gsbedit.py in the GSBEdit base directory.
"""


import wx
from copy import deepcopy as copy
import ui_elements.attribute_panel as ap
import ui_elements.spacer_panel as sp
import ui_elements.additional_panel as adp

import sys


class DisplayPanel(wx.Panel):
    """Displays a group of AttributePanels.
    
    Call set_panels once after creation and then set_module whenever a new
    module is loaded. Calls to update_display due to condition outcomes
    changing will be handled automatically.
    """

    def __init__(self, parent, panel_list, next_id, base_parent=None, prevent_freeze = False):
        if base_parent == None: base_parent = parent
        wx.Panel.__init__(self, base_parent)
        self.panel_list = []
        self.panel_sizer = wx.BoxSizer(wx.VERTICAL)
        self.parent = parent
        self.prevent_freeze = prevent_freeze
        
        self.module_info = {}
        self.module_values = {}
        self.changed_module_values = {}
        self.conditions = []
        
        self.SetSizer(self.panel_sizer)
        
        self.next_id = self.set_panels(panel_list, next_id)
    
    
    def get_next_id(self):
        return self.next_id
    
    
    def set_panels(self, panel_list, next_id):
        """Set the panels to be displayed.
        
        Once a panel list has been passed in, this class owns it. Do not
        attempt to make any modifications from outside of this class.
        """
        if not(self.prevent_freeze): self.Freeze()
        #self.panel_list = copy(panel_list)
        self.panel_list = panel_list
        
        for panel in self.panel_list:
            if panel['type'] == 'attributes':
                panel['object'] = ap.AttributePanel(self, panel, next_id, prevent_freeze = True)
                next_id = panel['object'].get_next_id()
            elif panel['type'] == 'additional':
                panel['object'] = adp.AdditionalPanel(self, panel, next_id)
                next_id = panel['object'].get_next_id()
            elif panel['type'] == 'spacer':
                panel['object'] = sp.SpacerPanel(self, panel)
            else:
                print >> sys.stderr, "Invalid panel type: %s" % panel['type']
                exit(1)
            panel['object'].Hide()
        
        if not(self.prevent_freeze): self.Thaw()
        
        return next_id
    
    
    def set_module(self, module_info, module_values):
        # Don't bother making a local copy. It's not needed for the module
        # info, and we *want* value changes to be seen globally.
        self.modue_info = module_info
        self.module_values = module_values
        self.changed_module_values = {}
        
        for panel in self.panel_list:
            panel['object'].set_module(module_info, module_values)
            if 'condition' in panel:
                self.conditions.append(panel['condition'])
        
        self.update_display()
    
    
    def update_display(self):
        """Update the displayed panels based on the supplied module
        information.
        """
        if not(self.prevent_freeze): self.Freeze()
        self.panel_sizer.Clear()
        
        for panel in self.panel_list:
            sd = self.should_display_child(panel)
            if sd in [True, None]:
                self.panel_sizer.Add(panel['object'], 0, wx.EXPAND)
                panel['object'].Show()
            else:
                panel['object'].Hide()
        
        if not(self.prevent_freeze): self.Thaw()
        self.Layout()


    def evaluate_condition(self, condition, values=None):
        if values == None:
            values = self.get_current_values()
        
        key, op, value = condition.split(' ')
        
        # For now we only deal with equality
        if op == '=':
            if key in values:
                if values[key] == value:
                    return True
            else:
                print >> sys.stderr, "Unknown key: %s" % key
            
        else:
            print >> sys.stderr, "Invalid condition: %s" % condition
            
        return False
    
    
    def should_display(self):
        """Check if this panel should be displayed.
        
        Check all eligible child panels and if any should be displayed then
        this panel should be as well.
        """
        display = False
        
        for panel in self.panel_list:
            dc = self.should_display_child(panel)
            if dc == True:
                display = True
        return display
    
    
    def should_display_child(self, panel):
        """Check if a child panel should be displayed.
        
        Internal function, not intended for external callers.
        """
        t1 = True
        if 'condition' in panel:
            t1 = self.evaluate_condition(panel['condition'])
        
        t2 = panel['object'].should_display()
        
        if t2 == None: return None
        
        return t1 and t2
    
    
    def evt_value_updated(self, key, value, sender=None):
        previous_values = self.get_current_values()
        new_values = copy(previous_values)
        self.update_dict(new_values, {key:value})
    
        if key in self.module_values:
            # A value we have an original for
            original_value = self.module_values[key]
            if key in self.changed_module_values:
                # Has been previously changed
                old_value = self.changed_module_values[key]
                
                if value == original_value:
                    # Changed back to the original
                    del self.changed_module_values[key]
                else:
                    # Changed from the original
                    self.changed_module_values[key] = value
            
            else:
                # Hasn't been previously changed
                if value == original_value:
                    # Hasn't actually changed (e.g. control creation)
                    pass
                else:
                    # First change for this value
                    self.changed_module_values[key] = value
        else:
            # A new value. We don't care if it's already in
            # self.changed_module_values, we need to store it.
            self.changed_module_values[key] = value        
        # Do our own checks regarding whether to call update_display
        if self.has_condition_changed(previous_values, new_values):
            self.update_display()
        
        # Pass the event along the chain
        if sender != self.parent:
            self.parent.evt_value_updated(key, value, self)
        
        for panel in self.panel_list:
            if panel['object'] != sender:
                panel['object'].evt_value_updated(key, value, self)


    def have_values_changed(self):
        return len(self.get_changed_values()) > 0
    
    
    def get_changed_values(self):
        return copy(self.changed_module_values)
    
    
    def get_current_values(self):
        new = copy(self.module_values)
        self.update_dict(new, self.get_changed_values())
        return new
    
    
    def has_condition_changed(self, previous_values, new_values):
        for condition in self.conditions:
            prev_result = self.evaluate_condition(condition, previous_values)
            new_result = self.evaluate_condition(condition, new_values)
            if prev_result != new_result:
                return True
        return False
    
    
    def update_dict(self, target, new_values):
        for key,value in new_values.iteritems():
            if value == None and key in target:
                del target[key]
            else:
                target[key] = value



    def save_values(self):
        self.update_dict(self.module_values, self.get_changed_values())
        self.changed_module_values = {}
        for panel in self.panel_list:
            panel.save_values()
    
    """
    def restore_values(self):
        for key,value in self.current_values.iteritems():
            if key in self.controls:
                self.controls[key] = value
    """
    
    def reset_displayed_values(self):
        for panel in self.panel_list:
            panel['object'].reset_displayed_values()
